package com.yeskery.nut.core;

import com.yeskery.nut.application.SecureKeyType;
import com.yeskery.nut.application.ServerType;
import com.yeskery.nut.util.StringUtils;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Nut 基础配置信息类
 * @author sprout
 * 2019-03-20 11:04
 * @version 1.0
 */
public abstract class BaseNutConfigure implements BasicNutConfigure {

	/** 多值分隔符 */
	private static final String MULTI_VALUE_SEPARATOR = ",";

	/** 配置的前缀 */
	protected static final String PREFIX = "server.";

	/** 环境 */
	protected static final String CONFIGURE_ENV_NAME = "env";

	/** 端口号 */
	protected static final String CONFIGURE_PORT_NAME = "port";

	/** 请求最大限制字节数 */
	protected static final String CONFIGURE_MAX_REQUEST_SIZE_NAME = "maxRequestSize";

	/** 是否以HTTPS启动 */
	protected static final String CONFIGURE_SECURE_NAME = "secure";

	/** 安全证书类型 */
	protected static final String CONFIGURE_SECURE_KEY_TYPE_NAME = "secureKeyType";

	/** jks证书库路径 */
	protected static final String CONFIGURE_JKS_PATH_NAME = "jksPath";

	/** jks证书库密码 */
	protected static final String CONFIGURE_JKS_PASSWORD_NAME = "jksPassword";

	/** pem公钥证书路径 */
	protected static final String CONFIGURE_PEM_PUBLIC_KEY_PATH = "pemPublicKeyPath";

	/** pem私钥证书路径 */
	protected static final String CONFIGURE_PEM_PRIVATE_KEY_PATH = "pemPrivateKeyPath";

	/** 静态资源的目录 */
	protected static final String CONFIGURE_STATIC_DIR_NAME = "staticDir";

	/** 静态资源路径 */
	protected static final String CONFIGURE_STATIC_PATH_NAME = "staticPath";

	/** 静态资源路径 */
	protected static final String CONFIGURE_STATIC_DIRECTORY_PREVIEW_NAME = "staticDirPreview";

	/** 是否开启脚本 */
	protected static final String CONFIGURE_ENABLE_SCRIPT_NAME = "enableScript";

	/** 脚本资源的文件路径 */
	protected static final String CONFIGURE_SCRIPT_PATH_NAME = "scriptPath";

	/** 脚本资源的文件编码 */
	protected static final String CONFIGURE_SCRIPT_ENCODING_NAME = "scriptEncoding";

	/** 是否开启脚本文件监听 */
	protected static final String CONFIGURE_ENABLE_SCRIPT_WATCH_NAME = "enableScriptWatch";

	/** 日志处理器 */
	protected static final String CONFIGURE_LOG_HANDLERS_NAME = "logHandlers";

	/** 日志格式化器 */
	protected static final String CONFIGURE_LOG_FORMATTER_NAME = "logFormatter";

	/** 日志等级 */
	protected static final String CONFIGURE_LOG_LEVEL_NAME = "logLevel";

	/** 日志保存目录 */
	protected static final String CONFIGURE_LOG_DIR_NAME = "logDir";

	/** 是否打印Banner */
	protected static final String CONFIGURE_PRINT_BANNER_NAME = "printBanner";

	/** 用于承载配置信息数据的对象 */
	private Map<String, String> configurationMap;

	/** 注册系统所有需要的配置key */
	private final Set<String> configurationKeySet;

	{
		configurationKeySet = new HashSet<>();
		configurationKeySet.add(PREFIX + CONFIGURE_ENV_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_PORT_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_MAX_REQUEST_SIZE_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_SECURE_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_SECURE_KEY_TYPE_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_JKS_PATH_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_JKS_PASSWORD_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_PEM_PUBLIC_KEY_PATH);
		configurationKeySet.add(PREFIX + CONFIGURE_PEM_PRIVATE_KEY_PATH);
		configurationKeySet.add(PREFIX + CONFIGURE_STATIC_DIR_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_STATIC_PATH_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_STATIC_DIRECTORY_PREVIEW_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_ENABLE_SCRIPT_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_SCRIPT_PATH_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_SCRIPT_ENCODING_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_LOG_HANDLERS_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_LOG_FORMATTER_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_LOG_LEVEL_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_LOG_DIR_NAME);
		configurationKeySet.add(PREFIX + CONFIGURE_PRINT_BANNER_NAME);
	}

	@Override
	public String getEnv() {
		String env = getConfigurationMap().get(PREFIX + CONFIGURE_ENV_NAME);
		return StringUtils.isEmpty(env) ? null : env;
	}

	@Override
	public Integer getServerPort() {
		String port = getConfigurationMap().get(PREFIX + CONFIGURE_PORT_NAME);
		return StringUtils.isEmpty(port) ? null : Integer.parseInt(port);
	}

	@Override
	public Long getMaxRequestSize() {
		String maxRequestSize = getConfigurationMap().get(PREFIX + CONFIGURE_MAX_REQUEST_SIZE_NAME);
		return StringUtils.isEmpty(maxRequestSize) ? null : Long.parseLong(maxRequestSize);
	}

	@Override
	public Boolean isServerSecurity() {
		String security = getConfigurationMap().get(PREFIX + CONFIGURE_SECURE_NAME);
		return StringUtils.isEmpty(security) ? null : Boolean.parseBoolean(security);
	}

	@Override
	public SecureKeyType getSecureKeyType() {
		String secureType = getConfigurationMap().get(PREFIX + CONFIGURE_SECURE_KEY_TYPE_NAME);
		try {
			return StringUtils.isEmpty(secureType) ? null : SecureKeyType.valueOf(secureType.toUpperCase());
		} catch (IllegalArgumentException e) {
			throw new NutException("UnSupport Secure Key Type [" + secureType.toUpperCase() +"]", e);
		}
	}

	@Override
	public String getJksPath() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_JKS_PATH_NAME);
	}

	@Override
	public String getJksPassword() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_JKS_PASSWORD_NAME);
	}

	@Override
	public String getPemPublicKeyPath() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_PEM_PUBLIC_KEY_PATH);
	}

	@Override
	public String getPemPrivateKeyPath() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_PEM_PRIVATE_KEY_PATH);
	}

	@Override
	public String[] getStaticResourceDirectory() {
		String staticResourceDirectory = getConfigurationMap().get(PREFIX + CONFIGURE_STATIC_DIR_NAME);
		if (StringUtils.isEmpty(staticResourceDirectory)) {
			return new String[0];
		}
		return staticResourceDirectory.split(MULTI_VALUE_SEPARATOR);
	}

	@Override
	public String[] getStaticResourcePaths() {
		String staticResourcePath = getConfigurationMap().get(PREFIX + CONFIGURE_STATIC_PATH_NAME);
		if (StringUtils.isEmpty(staticResourcePath)) {
			return new String[0];
		}
		return staticResourcePath.split(MULTI_VALUE_SEPARATOR);
	}

	@Override
	public boolean isEnableStaticDirectoryPreview() {
		String enableScript = getConfigurationMap().get(PREFIX + CONFIGURE_STATIC_DIRECTORY_PREVIEW_NAME);
		if (StringUtils.isEmpty(enableScript)) {
			return false;
		}
		return Boolean.TRUE.toString().equalsIgnoreCase(enableScript);
	}

	@Override
	public boolean isEnableScript() {
		String enableScript = getConfigurationMap().get(PREFIX + CONFIGURE_ENABLE_SCRIPT_NAME);
		if (StringUtils.isEmpty(enableScript)) {
			return false;
		}
		return Boolean.TRUE.toString().equalsIgnoreCase(enableScript);
	}

	@Override
	public String[] getScriptPath() {
		String scriptPath = getConfigurationMap().get(PREFIX + CONFIGURE_SCRIPT_PATH_NAME);
		if (StringUtils.isEmpty(scriptPath)) {
			return new String[0];
		}
		return scriptPath.split(MULTI_VALUE_SEPARATOR);
	}

	@Override
	public Charset getScriptEncoding() {
		String scriptEncoding = getConfigurationMap().get(PREFIX + CONFIGURE_SCRIPT_ENCODING_NAME);
		if (StringUtils.isEmpty(scriptEncoding)) {
			return StandardCharsets.UTF_8;
		}
		return Charset.forName(scriptEncoding);
	}

	@Override
	public boolean isEnableScriptWatch() {
		String enableScript = getConfigurationMap().get(PREFIX + CONFIGURE_ENABLE_SCRIPT_WATCH_NAME);
		if (StringUtils.isEmpty(enableScript)) {
			return false;
		}
		return Boolean.TRUE.toString().equalsIgnoreCase(enableScript);
	}

	@Override
	public ServerType getServerType() {
		return BasicNutConfigure.super.getServerType();
	}

	@Override
	public String getLogHandlers() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_LOG_HANDLERS_NAME);
	}

	@Override
	public String getLogFormatter() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_LOG_FORMATTER_NAME);
	}

	@Override
	public String getLogLevel() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_LOG_LEVEL_NAME);
	}

	@Override
	public String getLogDir() {
		return getConfigurationMap().get(PREFIX + CONFIGURE_LOG_DIR_NAME);
	}

	@Override
	public boolean isPrintBanner() {
		String printBanner = getConfigurationMap().get(PREFIX + CONFIGURE_PRINT_BANNER_NAME);
		if (StringUtils.isEmpty(printBanner)) {
			return true;
		}
		return Boolean.TRUE.toString().equalsIgnoreCase(printBanner);
	}

	/**
	 * 初始化
	 */
	public void initialize() {
		configurationMap = getConfiguration();
	}

	/**
	 * 获取以 {@link #PREFIX} 开头的所有配置信息类
	 * @return 以 {@link #PREFIX} 开头的所有配置信息类
	 */
	protected abstract Map<String, String> getConfiguration();

	/**
	 * 获取注册系统所有需要的配置key
	 * @return 注册系统所有需要的配置key
	 */
	protected Set<String> getConfigurationKeySet() {
		return configurationKeySet;
	}

	/**
	 * 获取配置map
	 * @return 配置map
	 */
	private Map<String, String> getConfigurationMap() {
		if (configurationMap == null) {
			throw new NutException("Nut Configure Not Initialized.");
		}
		return configurationMap;
	}
}
